﻿using TwitterLikeWebChatWcfService;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting.Web;
using System.Net;
using System.Text;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Json;

namespace TwitterLikeWebChatWcfService.Test
{    
    [TestClass()]
    public class TwitterServiceTest
    {
        // NO! This is shit. Two types of techniques for testing code mixed. Fucked up!

        // nakov
        static string databaseDirectory;

        public const string PRODUCTION_DB_NAME = "TwitterLikeWebChatDB";
        public const string TEST_DB_NAME = "TwitterLikeWebChatDB_Test";
        public const string CLONE_CONNECTION_STRING =
            @"Data Source=.\SQLEXPRESS;Initial Catalog=master;Integrated Security=True;MultipleActiveResultSets=True";

        [ClassInitialize]
        public static void InitTestDatabase(TestContext testContext)
        {
            databaseDirectory = testContext.TestDeploymentDir;
            CloneDatabase();

            testChannelName = CreateUniqueChannelName();
            InvokeServiceHttpPost("CreateNewChannel", testChannelName);

            TwitterService target = new TwitterService();
            target.CreateNewChannel(testChannelName);
        }

        private static void CloneDatabase()
        {
            // Clone the production database over the testing database
            string sqlCloneProductionDB = String.Format(@"
				USE master

				BACKUP DATABASE {0} TO DISK='{1}\{0}.bak'

				IF EXISTS(SELECT * FROM sys.sysdatabases where name='{2}')
				BEGIN
					ALTER DATABASE {2} SET SINGLE_USER WITH ROLLBACK IMMEDIATE
					DROP DATABASE {2}
				END

				RESTORE DATABASE {2} FROM DISK='{1}\{0}.bak'
				WITH
				MOVE '{0}' TO '{1}\{2}.mdf',
				MOVE '{0}_log' TO '{1}\{2}_Log.ldf' ",
                PRODUCTION_DB_NAME, databaseDirectory, TEST_DB_NAME);

            SqlConnection dbConnection = new SqlConnection(CLONE_CONNECTION_STRING);
            dbConnection.Open();

            using (dbConnection)
            {
                SqlCommand cloneDBCmd = new SqlCommand(sqlCloneProductionDB, dbConnection);
                cloneDBCmd.ExecuteNonQuery();
            }
        }

        private static string alphanumericsString = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";

        private static Random random = new Random();

        private static string CreateUniqueChannelName()
        {
            const int idLength = 30;

            string channelName = "ChannelName_" + CreateRandomString(30);

            string channelNamesString = InvokeServiceHttpGet("GetChannelNames");

            if (channelNamesString.Contains(channelName))
            {
                return CreateUniqueChannelName();
            }
            else
            {
                return channelName;
            }
        }

        private static string CreateRandomString(int length)
        {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < length; i++)
            {
                int r = random.Next(0, alphanumericsString.Length);
                char c = alphanumericsString[r];

                sb.Append(c);
            }

            string randomString = sb.ToString();
            return randomString;
        }

        private static string testChannelName;

        [ClassCleanup]
        public static void CleanupTestDatabase()
        {
            // Drop the testing database            
            string sqlDropTestDatabaseCommandText = String.Format(@"
				USE master

				IF EXISTS(SELECT * FROM sys.sysdatabases where name='{0}')
				BEGIN
					ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE
					DROP DATABASE {0}
				END",
                TEST_DB_NAME);

            SqlConnection dbConnection = new SqlConnection(CLONE_CONNECTION_STRING);
            dbConnection.Open();

            using (dbConnection)
            {
                SqlCommand cloneDBCmd = new SqlCommand(sqlDropTestDatabaseCommandText, dbConnection);
                cloneDBCmd.ExecuteNonQuery();
            }

            InvokeServiceHttpPost("DeleteChannel", testChannelName);
        }

        // nakov
        public const string ServiceBaseUrl = "http://localhost:1355/TwitterService.svc/";
        
        private static string InvokeServiceHttpGet(string relativeUrl)
        {
            WebClient webClient = new WebClient();
            byte[] serviceResponse = webClient.DownloadData(ServiceBaseUrl + relativeUrl);
            string serviceResponseStr = Encoding.UTF8.GetString(serviceResponse);
            return serviceResponseStr; // vs webClient.DownloadString(ServiceBaseUrl + relativeUrl);
        }

        private static string InvokeServiceHttpPost(string relativeUrl, object objectToPost)
        {
            string url = ServiceBaseUrl + relativeUrl;
            HttpWebRequest httpRequest = WebRequest.Create(url) as HttpWebRequest;
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/json";
            Stream httpRequestStream = httpRequest.GetRequestStream();
            using (httpRequestStream)
            {
                string jsonToPost = ConvertToJSON(objectToPost);
                byte[] bytesToPost = UTF8Encoding.UTF8.GetBytes(jsonToPost);
                httpRequestStream.Write(bytesToPost, 0, bytesToPost.Length);
            }
            HttpWebResponse httpResponse = httpRequest.GetResponse() as HttpWebResponse;
            StreamReader httpResponseReader = new StreamReader(httpResponse.GetResponseStream());
            using (httpResponseReader)
            {
                string httpResponseBody = httpResponseReader.ReadToEnd();
                return httpResponseBody;
            }
        }

        private static string ConvertToJSON(object obj)
        {
            DataContractJsonSerializer jsonSerializer =
                new DataContractJsonSerializer(obj.GetType());
            MemoryStream jsonStream = new MemoryStream();
            jsonSerializer.WriteObject(jsonStream, obj);
            byte[] jsonData = jsonStream.ToArray();
            string jsonString = Encoding.UTF8.GetString(jsonData);
            return jsonString;
        }

        [TestMethod()]
        [UrlToTest("http://localhost:1355/")]
        public void UploadMessageByChannelNameTest()
        {
            string messageContent = "message: " + CreateRandomString(30);

            TwitterService target = new TwitterService();
            MessageDTO messageDTO = new MessageDTO();
            messageDTO.MessageContent = messageContent;
            messageDTO.ChannelName = testChannelName;
            target.UploadMessageByChannelName(messageDTO);

            var messages = target.GetMessagesByChannelName(testChannelName);

            bool messageContentFound = false;

            foreach (var m in messages)
            {
                if (m.MessageContent == messageContent)
                {
                    messageContentFound = true;
                    break;
                }
            }

            Assert.IsTrue(messageContentFound);
        }

        [TestMethod]
        public void ContainsTestChannelNameTest()
        {
            var channelNamesString = InvokeServiceHttpGet("GetChannelNames");
            bool containsTestChannelName = channelNamesString.Contains("\"" + testChannelName + "\"");

            Assert.IsTrue(containsTestChannelName);
        }

        [TestMethod]
        public void CreateDeleteChannelTest()
        {            
            string newTestChannelName = CreateUniqueChannelName();
            InvokeServiceHttpPost("CreateNewChannel", newTestChannelName);
            var channelNamesString = InvokeServiceHttpGet("GetChannelNames");
            bool containsNewTestChannelName = channelNamesString.Contains("\"" + newTestChannelName + "\"");
            Assert.IsTrue(containsNewTestChannelName);

            InvokeServiceHttpPost("DeleteChannel", newTestChannelName);            
            channelNamesString = InvokeServiceHttpGet("GetChannelNames");
            containsNewTestChannelName = channelNamesString.Contains("\"" + newTestChannelName + "\"");
            Assert.IsFalse(containsNewTestChannelName);
        }
    }
}
